//
// Created by 高曦祥 on 2023/11/25.
//

#include "util/memory_pool.h"

namespace TinyDB {

t_byte *MemoryPool::Allocate(t_uint64 size) {
  assert(size > 0);
  t_byte* res = nullptr;
  if (size < byte_remaining_) {
    res              = alloc_ptr_;
    alloc_ptr_      += size;
    byte_remaining_ -= size;
    return res;
  }
  return allocateFallback(size);
}

MemoryPool::MemoryPool() {
  alloc_ptr_      = nullptr;
  byte_remaining_ = 0;
}

MemoryPool::~MemoryPool() {
  for (auto & block : blocks_) {
    delete[] block;
  }
}

t_byte* MemoryPool::allocateFallback(t_uint64 size) {
  assert(size > 0);
  t_byte* res = nullptr;
  if (size > kBlockSize / 4) {
    res = allocateNewBlock(size);
    return res;
  }
  res = allocateNewBlock(kBlockSize);
  byte_remaining_ = kBlockSize - size;
  alloc_ptr_      = res + size;
  return res;
}

t_byte* MemoryPool::allocateNewBlock(t_uint64 size) {
  auto* blk = new t_char[size];
  blocks_.push_back(blk);
  return blk;
}

t_byte *MemoryPool::AllocateAlign(t_uint64 size) {
  t_uint32 align = (sizeof(void *) > 8) ? sizeof(void *) : 8;
  assert((align & align) - 1 != 0);
  t_uint64 cur_mod = reinterpret_cast<t_uintptr>(alloc_ptr_) & (align - 1);
  t_uint64 slot    = cur_mod == 0 ? 0 : (align - cur_mod);
  t_uint64 need    = size + slot;
  t_byte*  res     = nullptr;

  if (byte_remaining_ > need) {
    res              = alloc_ptr_ + need;
    alloc_ptr_      += need;
    byte_remaining_ -= need;
  } else {
    res = allocateFallback(size);
  }
  return res;
}

}